import {error, find, isValid, Node, Vec3} from "cc";
import {TipController} from "db://assets/scripts/ui/common/TipController";
import {FightTipController} from "db://assets/scripts/ui/common/FightTipController";
import {FightTipEnum} from "db://assets/scripts/ui/common/FightTipEnum";
import {PriorityEnum} from "db://assets/scripts/game/PriorityEnum";
import {ResourceUtils} from "db://assets/scripts/common/ResourceUtils";
import {GameManager} from "db://assets/scripts/game/GameManager";
import {UITipType} from "db://assets/scripts/game/UITipType";
import {PoolManager} from "db://assets/scripts/common/PoolManager";
import {Dialog} from "db://assets/scripts/common/Dialog";
import {PlayerBloodBarController} from "db://assets/scripts/game/role/player/PlayerBloodBarController";
import {PlayerController} from "db://assets/scripts/game/role/player/PlayerController";
import {MonsterBloodBarController} from "db://assets/scripts/game/role/monster/MonsterBloodBarController";
import {MonsterController} from "db://assets/scripts/game/role/monster/MonsterController";

/**
 * UI管理器
 */
export class UIManager {
    /**
     * 管理所有弹窗
     */
    private dialogs: Map<string, Node> = new Map<string, Node>();
    /**
     * 正在加载的弹窗
     */
    private loadingDialogs: Map<string, boolean> = new Map<string, boolean>();
    /**
     * 正在等待显示的弹窗
     */
    private waitingDialogs: Dialog[] = [];
    /**
     * 提示的持续时间
     */
    private showTipTime: number = 0;
    /**
     * 文字显示的间隔时间
     */
    private tipInterval = 800;
    /**
     * 血条距离玩家节点位置
     */
    private playerBloodOffsetPosition = new Vec3(-10, 100, 0);
    /**
     * 玩家血条缩放大小
     */
    private playerBloodScale = new Vec3(1.5, 1.5, 1.5);
    /**
     * 血条距离小怪节点位置
     */
    private monsterBloodOffsetPosition = new Vec3(-10, 100, 0);
    /**
     * 小怪血条缩放大小
     */
    private monsterBloodScale = new Vec3(1.5, 1.5, 1.5);

    /**
     * UI管理器实例
     */
    private static INSTANCE: UIManager;

    /**
     * 获取UI管理器实例
     */
    public static getInstance() {
        if (this.INSTANCE) {
            return this.INSTANCE;
        }
        this.INSTANCE = new UIManager();
        return this.INSTANCE;
    }

    /**
     * 查询指定弹窗是否正在展示
     *
     * @param path 弹窗路径
     */
    public dialogVisible(path: string): boolean {
        if (!this.dialogs.has(path)) {
            return false;
        }
        let sharedPanel = this.dialogs.get(path);
        return isValid(sharedPanel) && sharedPanel.active && isValid(sharedPanel.parent);
    }

    /**
     * 展示弹窗
     *
     * @param path 弹窗路径
     * @param args 参数
     * @param callback 回调
     * @param priority 界面优先级
     */
    public showDialog(path: string, args?: any, callback?: Function, priority: number = PriorityEnum.NORMAL) {
        // 当该弹窗正在加载中时
        if (this.loadingDialogs.has(path)) {
            // 直接返回
            return;
        }

        let index = path.lastIndexOf("/");
        let scriptName = path.slice(index + 1);
        if (!args) {
            args = [];
        }

        // 已缓存该弹窗
        if (this.dialogs.has(path)) {
            // 把弹窗取出来
            let panel = this.dialogs.get(path);
            // 当弹窗可用时
            if (isValid(panel)) {
                panel.parent = find("Canvas");
                panel.active = true;

                let script1 = panel.getComponent(scriptName);
                let script2 = panel.getComponent(scriptName.charAt(0).toUpperCase() + scriptName.slice(1));

                if (script1 && script1.hasOwnProperty("show")) {
                    script1["show"](script1, args);
                    callback && callback(script1);
                } else if (script2 && script2.hasOwnProperty("show")) {
                    script2["show"](script2, args);
                    callback && callback(script2);
                } else {
                    error(`查找不到脚本文件${scriptName}`);
                }
                return;
            }
        }

        // 记录正在加载的弹窗
        this.loadingDialogs.set(path, true);
        ResourceUtils.createUI(path, (err, node) => {
            // 如果刚刚正要展示的弹窗被关闭，那么就不需要加载了
            let closed = !this.loadingDialogs.has(path);
            // 不加载弹窗
            this.loadingDialogs.set(path, false);
            if (err) {
                error(err);
                return;
            }

            // 设置界面优先级
            node.setSiblingIndex(priority);

            // 缓存弹窗
            this.dialogs.set(path, node);

            let script1 = node.getComponent(scriptName);
            let script2 = node.getComponent(scriptName.charAt(0).toUpperCase() + scriptName.slice(1));

            if (script1 && script1.hasOwnProperty("show")) {
                script1["show"](script1, args);
                callback && callback(script1);
            } else if (script2 && script2.hasOwnProperty("show")) {
                script2["show"](script2, args);
                callback && callback(script2);
            } else {
                error(`查找不到脚本文件${scriptName}`);
            }

            // 当刚刚展示的弹窗又被关闭时
            if (closed) {
                // 关闭弹窗
                this.hideDialog(path);
            }
        });
    }

    /**
     * 隐藏弹窗
     *
     * @param path 弹窗路径
     * @param callback 回调
     */
    public hideDialog(path: string, callback?: Function) {
        // 未缓存该弹窗
        if (!this.dialogs.has(path)) {
            // 不加载该弹窗
            this.loadingDialogs.set(path, false);
            return;
        }

        // 获取弹窗
        let panel = this.dialogs.get(path);
        if (isValid(panel)) {
            let animation = panel.getComponent("animationUI");
            if (animation) {
                animation["close"](() => {
                    panel.parent = null;
                });
            } else {
                panel.parent = null;
            }
        }

        callback && callback();
    }

    /**
     * 添加弹窗
     *
     * @param path 弹窗路径
     * @param script 脚本名称
     * @param param 参数
     */
    public addDialog(path: string, script: string, param: any) {
        let dialog: Dialog = {
            path: path,
            script: script,
            param: param,
            displayed: false
        };
        this.waitingDialogs.push(dialog);
        this.refreshWaitingDialogs();
    }

    /**
     * 插入弹窗
     *
     * @param index 索引
     * @param path 弹窗路径
     * @param script 脚本名称
     * @param param 参数
     */
    public insertDialog(index: number, path: string, script: string, param: any) {
        let dialog: Dialog = {
            path: path,
            script: script,
            param: param,
            displayed: false
        };
        this.waitingDialogs.splice(index, 0, dialog);
    }

    /**
     * 隐藏弹窗，并从等待列表移除
     *
     * @param path 弹窗路径
     */
    public shiftDialog(path: string) {
        this.hideDialog(path, () => {
            let first = this.waitingDialogs[0];
            if (first && first.path === path) {
                this.waitingDialogs.shift();
                this.refreshWaitingDialogs();
            }
        });
    }

    /**
     * 展示等待列表中还未展示的弹窗
     */
    public refreshWaitingDialogs() {
        if (this.waitingDialogs.length > 0) {
            let firstDialog = this.waitingDialogs[0];
            // 没展示过
            if (!firstDialog.displayed) {
                // 展示弹窗
                this.showDialog(firstDialog.path, firstDialog.param);
                // 记录该弹窗展示过
                this.waitingDialogs[0].displayed = true;
            }
        }
    }

    /**
     * 展示提示信息
     *
     * @param content 提示内容
     * @param type 类型
     * @param position 目标位置
     * @param scale 缩放比例
     * @param callback 回调
     */
    public showTip(content: string,
                   type: UITipType = UITipType.TXT,
                   position: Vec3 = new Vec3(),
                   scale: number = 1,
                   callback?: Function) {
        // 当前时间
        let now = Date.now();
        // 当前时间-显示时间 < 间隔时间
        if (now - this.showTipTime < this.tipInterval && type === UITipType.TXT) {
            // 空余时间
            let deltaTime = this.tipInterval - (now - this.showTipTime);
            // 等下一个间隔
            setTimeout(() => {
                // 播放提示动画
                this.showTipAnimation(content, type, position, scale, callback);
            }, deltaTime);
            // 记录提示的显示时间
            this.showTipTime = now + deltaTime;
        } else {
            // 播放提示动画
            this.showTipAnimation(content, type, position, scale, callback);
            this.showTipTime = now;
        }
    }

    /**
     * 播放提示动画
     *
     * @param content 提示内容
     * @param type 提示类型
     * @param position 提示位置
     * @param scale 提示缩放比
     * @param callback 回调
     * @private
     */
    private showTipAnimation(content: string,
                             type: UITipType,
                             position: Vec3,
                             scale: number,
                             callback?: Function) {
        ResourceUtils.loadUIPrefab('common/tips')
            .then((prefab) => {
                let tipNode = PoolManager.getInstance().getNode(prefab, find("Canvas"));
                let tipController = tipNode.getComponent(TipController);
                tipController.show(content, type, position, scale, callback);
            });
    }

    /**
     * 显示玩家血条
     *
     * @param scriptParent 使用者的脚本
     * @param totalBlood 总血量
     * @param currentBlood 当前血量
     * @param offsetPosition 血条偏移量
     * @param scale 缩放比
     * @param callback 回调
     */
    public showPlayerBloodBar(scriptParent: PlayerController,
                              totalBlood: number,
                              currentBlood: number,
                              offsetPosition: Vec3,
                              scale: Vec3,
                              callback?: Function) {
        ResourceUtils.loadUIPrefab("fight/playerBloodBar").then((prefab) => {
            let bloodBarNode = PoolManager.getInstance().getNode(prefab, find("Canvas"));
            bloodBarNode.setSiblingIndex(PriorityEnum.ZERO);
            let script = bloodBarNode.getComponent(PlayerBloodBarController);
            scriptParent.bloodBarScript = script;
            script.show(scriptParent, totalBlood, currentBlood, offsetPosition, scale, callback);
        });
    }

    /**
     * 显示怪物血条
     *
     * @param scriptParent 使用者的脚本
     * @param totalBlood 总血量
     * @param offsetPosition 血条偏移量
     * @param scale 缩放比
     * @param callback 回调
     */
    public showMonsterBloodBar(scriptParent: MonsterController,
                               totalBlood: number,
                               offsetPosition: Vec3,
                               scale: Vec3,
                               callback?: Function) {
        ResourceUtils.loadUIPrefab("fight/monsterBloodBar").then((prefab) => {
            let bloodBarNode = PoolManager.getInstance().getNode(prefab, find("Canvas"));
            let script = bloodBarNode.getComponent(MonsterBloodBarController);
            scriptParent.bloodBarScript = script;
            script.show(scriptParent, totalBlood, offsetPosition, scale, true, callback);
        });
    }

    /**
     * 血量提示
     *
     * @param scriptParent 使用者的脚本
     * @param type 总血量
     * @param blood 当前血量
     * @param offsetPosition 血条偏移量
     * @param callback 回调
     */
    public showBloodTip(scriptParent: PlayerController|MonsterController,
                        type: FightTipEnum,
                        blood: number,
                        offsetPosition: Vec3,
                        callback?: Function) {
        ResourceUtils.loadUIPrefab("common/fightTip").then((prefab) => {
            let tipNode = PoolManager.getInstance().getNode(prefab, find("Canvas"));
            let position = GameManager.mainCamera.convertToUINode(scriptParent.node.worldPosition, find("Canvas"));
            position.add(offsetPosition);
            tipNode.setPosition(position);

            let script = tipNode.getComponent(FightTipController);
            script.show(scriptParent, type, blood, callback);
        });
    }
}